Merge remote-tracking branch 'origin/master' into scenarios

Andrew Cantino 10 years ago
parent
commit
a0aef96673

+ 1 - 0
Gemfile

@@ -70,6 +70,7 @@ gem 'em-http-request', '~> 1.1.2'
70 70
 gem 'weibo_2', '~> 0.1.4'
71 71
 gem 'hipchat', '~> 1.1.0'
72 72
 gem 'xmpp4r',  '~> 0.5.6'
73
+gem 'slack-notifier', '~> 0.5.0'
73 74
 
74 75
 gem 'therubyracer', '~> 0.12.1'
75 76
 

+ 2 - 0
Gemfile.lock

@@ -245,6 +245,7 @@ GEM
245 245
       multi_json
246 246
       simplecov-html (~> 0.8.0)
247 247
     simplecov-html (0.8.0)
248
+    slack-notifier (0.5.0)
248 249
     slop (3.5.0)
249 250
     sprockets (2.11.0)
250 251
       hike (~> 1.2)
@@ -355,6 +356,7 @@ DEPENDENCIES
355 356
   sass-rails (~> 4.0.0)
356 357
   select2-rails (~> 3.5.4)
357 358
   shoulda-matchers
359
+  slack-notifier (~> 0.5.0)
358 360
   therubyracer (~> 0.12.1)
359 361
   twilio-ruby (~> 3.11.5)
360 362
   twitter (~> 5.8.0)

+ 19 - 4
app/concerns/liquid_interpolatable.rb

@@ -4,7 +4,7 @@ module LiquidInterpolatable
4 4
   def interpolate_options(options, payload)
5 5
     case options.class.to_s
6 6
     when 'String'
7
-      Liquid::Template.parse(options).render(payload)
7
+      interpolate_string(options, payload)
8 8
     when 'ActiveSupport::HashWithIndifferentAccess', 'Hash'
9 9
       duped_options = options.dup
10 10
       duped_options.each do |key, value|
@@ -18,17 +18,32 @@ module LiquidInterpolatable
18 18
   end
19 19
 
20 20
   def interpolate_string(string, payload)
21
-    Liquid::Template.parse(string).render(payload)
21
+    Liquid::Template.parse(string).render!(payload, registers: {agent: self})
22 22
   end
23 23
 
24 24
   require 'uri'
25 25
   # Percent encoding for URI conforming to RFC 3986.
26 26
   # Ref: http://tools.ietf.org/html/rfc3986#page-12
27
-  module Huginn
27
+  module Filters
28 28
     def uri_escape(string)
29 29
       CGI::escape string
30 30
     end
31 31
   end
32
+  Liquid::Template.register_filter(LiquidInterpolatable::Filters)
32 33
 
33
-  Liquid::Template.register_filter(LiquidInterpolatable::Huginn)
34
+  module Tags
35
+    class Credential < Liquid::Tag
36
+      def initialize(tag_name, name, tokens)
37
+        super
38
+        @credential_name = name.strip
39
+      end
40
+
41
+      def render(context)
42
+        credential = context.registers[:agent].credential(@credential_name)
43
+        raise "No user credential named '#{@credential_name}' defined" if credential.nil?
44
+        credential
45
+      end
46
+    end
47
+  end
48
+  Liquid::Template.register_tag('credential', LiquidInterpolatable::Tags::Credential)
34 49
 end

+ 66 - 0
app/models/agents/slack_agent.rb

@@ -0,0 +1,66 @@
1
+module Agents
2
+  class SlackAgent < Agent
3
+    include LiquidInterpolatable
4
+    cannot_be_scheduled!
5
+    cannot_create_events!
6
+
7
+    DEFAULT_WEBHOOK = 'incoming-webhook'
8
+    DEFAULT_USERNAME = 'Huginn'
9
+    description <<-MD
10
+      The SlackAgent lets you receive events and send notifications to [slack](https://slack.com/).
11
+
12
+      To get started, you will first need to setup an incoming webhook.
13
+      Go to, https://`your_team_name`.slack.com/services/new/incoming-webhook,
14
+      choose a default channel and add the integration.
15
+
16
+      Your webhook URL will look like:
17
+
18
+      https://`your_team_name`.slack.com/services/hooks/incoming-webhook?token=`your_auth_token`
19
+
20
+      Once the webhook has been setup it can be used to post to other channels or ping team members.
21
+      To send a private message to team-mate, assign his username as `@username` to the channel option.
22
+      To communicate with a different webhook on slack, assign your custom webhook name to the webhook option.
23
+      Messages can also be formatted using [Liquid](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid)
24
+    MD
25
+
26
+    def default_options
27
+      {
28
+        'team_name' => 'your_team_name',
29
+        'auth_token' => 'your_auth_token',
30
+        'channel' => '#general',
31
+        'username' => DEFAULT_USERNAME,
32
+        'message' => "Hey there, It's Huginn",
33
+        'webhook' => DEFAULT_WEBHOOK
34
+      }
35
+    end
36
+
37
+    def validate_options
38
+      errors.add(:base, "auth_token is required") unless options['auth_token'].present?
39
+      errors.add(:base, "team_name is required") unless options['team_name'].present?
40
+      errors.add(:base, "channel is required") unless options['channel'].present?
41
+    end
42
+
43
+    def working?
44
+      received_event_without_error?
45
+    end
46
+
47
+    def webhook
48
+      options[:webhook].presence || DEFAULT_WEBHOOK
49
+    end
50
+
51
+    def username
52
+      options[:username].presence || DEFAULT_USERNAME
53
+    end
54
+
55
+    def slack_notifier
56
+      @slack_notifier ||= Slack::Notifier.new(options[:team_name], options[:auth_token], webhook, username: username)
57
+    end
58
+
59
+    def receive(incoming_events)
60
+      incoming_events.each do |event|
61
+        opts = interpolate_options options, event.payload
62
+        slack_notifier.ping opts[:message], channel: opts[:channel], username: opts[:username]
63
+      end
64
+    end
65
+  end
66
+end

+ 64 - 0
spec/models/agents/slack_agent_spec.rb

@@ -0,0 +1,64 @@
1
+require 'spec_helper'
2
+require 'models/concerns/liquid_interpolatable'
3
+
4
+describe Agents::SlackAgent do
5
+  it_behaves_like LiquidInterpolatable
6
+
7
+  before(:each) do
8
+    @valid_params = {
9
+                      'auth_token' => 'token',
10
+                      'team_name' => 'testteam',
11
+                      'channel' => '#random',
12
+                      'username' => "{{username}}",
13
+                      'message' => "{{message}}"
14
+                    }
15
+
16
+    @checker = Agents::SlackAgent.new(:name => "slacker", :options => @valid_params)
17
+    @checker.user = users(:jane)
18
+    @checker.save!
19
+
20
+    @event = Event.new
21
+    @event.agent = agents(:bob_weather_agent)
22
+    @event.payload = { :channel => '#random', :message => 'Looks like its going to rain', username: "Huggin user"}
23
+    @event.save!
24
+  end
25
+
26
+  describe "validating" do
27
+    before do
28
+      expect(@checker).to be_valid
29
+    end
30
+
31
+    it "should require a auth_token" do
32
+      @checker.options['auth_token'] = nil
33
+      expect(@checker).not_to be_valid
34
+    end
35
+
36
+    it "should require a channel" do
37
+      @checker.options['channel'] = nil
38
+      expect(@checker).not_to be_valid
39
+    end
40
+
41
+    it "should require a team_name" do
42
+      @checker.options['team_name'] = 'nil'
43
+      expect(@checker).to be_valid
44
+    end
45
+  end
46
+  describe "#receive" do
47
+    it "receive an event without errors" do
48
+      any_instance_of(Slack::Notifier) do |obj|
49
+        mock(obj).ping(@event.payload[:message],
50
+                       channel: @event.payload[:channel],
51
+                       username: @event.payload[:username]
52
+                      )
53
+      end
54
+      expect(@checker.receive([@event])).to_not raise_error
55
+    end
56
+  end
57
+
58
+  describe "#working?" do
59
+    it "should call received_event_without_error?" do
60
+      mock(@checker).received_event_without_error?
61
+      @checker.working?
62
+    end
63
+  end
64
+end

+ 13 - 2
spec/models/concerns/liquid_interpolatable.rb

@@ -49,8 +49,19 @@ shared_examples_for LiquidInterpolatable do
49 49
     end
50 50
 
51 51
     it "should work for strings" do
52
-      @checker.send(:interpolate_string, "{{variable}}", @event.payload).should == "hello"
53
-      @checker.send(:interpolate_string, "{{variable}} you", @event.payload).should == "hello you"
52
+      @checker.interpolate_string("{{variable}}", @event.payload).should == "hello"
53
+      @checker.interpolate_string("{{variable}} you", @event.payload).should == "hello you"
54
+    end
55
+  end
56
+  describe "liquid tags" do
57
+    it "should work with existing credentials" do
58
+      @checker.interpolate_string("{% credential aws_key %}", {}).should == '2222222222-jane'
59
+    end
60
+
61
+    it "should raise an exception for undefined credentials" do
62
+      expect {
63
+        @checker.interpolate_string("{% credential unknown %}", {})
64
+      }.to raise_error
54 65
     end
55 66
   end
56 67
 end